<script lang="tsx">
import { ref, defineComponent } from 'vue';
import { ElTable, ElTableColumn, ElPagination } from 'element-plus';
import { getSlot } from '@/utils/basic-common/tsxHelper'
import { TableColumnRender, TableColumn, TableSlotDefault } from './types'
import ColumnRender from './TableColumnRender.vue'

type ComponentRef<T extends abstract new (...args: any) => any> = InstanceType<T>;
type Recordable<T = any, K extends string | number | symbol = string> = Record<K extends null | undefined ? string : K, T>

const pageSizes = [10, 20, 50, 100]

// 序号分页叠加
const setIndex = (reserveIndex: boolean, index: number, size: number, current: number) => {
  const newIndex = index + 1
  if (reserveIndex) {
    return size * (current - 1) + newIndex
  } else {
    return newIndex
  }
}

// 表格单元格扩展渲染
function renderColumnRender(
  row: Recordable,
  column: TableColumn,
  columnRender: TableColumnRender,
  columnRenderProps: any
) {
  return <ColumnRender row={row} column={column} columnRender={columnRender} columnRenderProps={columnRenderProps} />
}

export default defineComponent({
  name: 'CjuiTable',
  props: {
    // 基础配置
    loading: {
      type: Boolean,
      default: false
    },
    columns: {
      type: Array,
      default: () => []
    },
    data: {
      type: Array,
      default: () => []
    },
    height: {
      type: String,
      default: undefined
    },
    // 表格配置
    tableProps: {
      type: Object,
      default: () => ({})
    },
    // 表格扩展配置，是否叠加索引
    tableReserveIndex: {
      type: Boolean,
      default: true
    },
    // 表格扩展配置，外部容器border
    tableBorder: {
      type: Boolean,
      default: false
    },
    // 分页器
    pagination: {
      type: [Object, Boolean],
      default: () => {
        return {
          current: 1,
          size: 10,
          total: 0,
        }
      }
    },
    paginationProps: {
      type: Object,
      default: () => ({})
    },
  },
  emits: ['currentChange', 'sizeChange'],
  setup(props: any, { emit, expose, slots }) {
    const clientWidth = document.body.clientWidth

    // 表格渲染
    const elTableRef = ref<ComponentRef<typeof ElTable>>();
    function renderTableTreeColumn(columnsChildren: TableColumn[]) {
      return columnsChildren.map((col) => {
        const colProps = { ...col }
        if (colProps.children) delete colProps.children
        if (colProps.slots) delete colProps.slots
        return (
          <ElTableColumn
            show-overflow-tooltip={col.showOverflowTooltip || true}
            key={col.prop}
            {...colProps}
          >
            {{
              default: (data: TableSlotDefault) =>
                col.children && col.children.length
                  ? renderTableColumns(col.children)
                  : // @ts-ignore
                    getSlot(slots, col.prop, data) 
                    ||
                    (col.slots?.default && col.slots?.default(data))
                    ||
                    col?.formatter?.(data.row, data.column, data.row[col.prop], data.$index) 
                    ||
                    (
                      col.columnRender ? 
                        renderColumnRender(data.row, data.column, col.columnRender, col.columnRenderProps) : 
                        data.row[col.prop]
                    ),
              // @ts-ignore
              header: getSlot(slots, `${col.prop}-header`) || (col.slots?.header && col.slots?.header())
            }}
          </ElTableColumn>
        )
      })
    }
    function renderTableColumns(columnsChildren?: TableColumn[]) {
      return (columnsChildren || props.columns).map((col: any) => {
        if (col.type === 'index') {
          return (
            <ElTableColumn
              type="index"
              {...col}
              width={col.width || '65px'}
              index={
                col.index
                  ? col.index
                  : (index: number) => setIndex(
                      props.tableReserveIndex,
                      index, 
                      props.pagination.size || 10, 
                      props.pagination.current || 1
                    )
              }>
            </ElTableColumn>
          )
        } else if (col.type === 'expand') {
          const colProps = { ...col }
          if (colProps.slots) delete colProps.slots
          return (
            <ElTableColumn type="expand" {...colProps}>
              {{
                // @ts-ignore
                default: (data: TableSlotDefault) => getSlot(slots, 'expand', data) || (col.slots?.default && col.slots?.default(data))
              }}
            </ElTableColumn>
          )
        } else if (col.type === 'selection') {
          return (
            <ElTableColumn
              type="selection"
              headerAlign={col.headerAlign || 'center'}
              align={col.align || 'center'}
              width={col.width || '50'}
              {...col}
            ></ElTableColumn>
          )
        } else {
          const colProps = { ...col }
          if (colProps.children) delete colProps.children
          if (colProps.slots) delete colProps.slots
          return (
            <ElTableColumn
              show-overflow-tooltip={col.showOverflowTooltip || true}
              key={col.prop}
              {...colProps}>
              {{
                default: (data: TableSlotDefault) => 
                  col.children && col.children.length
                    ? renderTableTreeColumn(col.children)
                    : getSlot(slots, col.prop, data)
                      ||
                      (col.slots?.default && col.slots?.default(data))
                      ||
                      col?.formatter?.(data.row, data.column, data.row[col.prop], data.$index) 
                      ||
                      (
                        col.columnRender ? 
                          renderColumnRender(data.row, data.column, col.columnRender, col.columnRenderProps) : 
                          data.row[col.prop]
                      ),
                header: () => getSlot(slots, `${col.prop}-header`) || (col.slots?.header && col.slots?.header()) || col.label
              }}
            </ElTableColumn>
          )
        }
      })
    }
    function renderTable() {
      return (
        <ElTable
          ref={elTableRef}
          data={props.data}
          height={props.height ? `calc(${props.height} - 52px)` : undefined}
          {...props.tableProps}
        >
          {{
            default: () => renderTableColumns(),
            // @ts-ignore
            append: () => getSlot(slots, 'append')
          }}
        </ElTable>
      )
    }

    // 分页器渲染
    function handleSizeChange(val: number) {
      // eslint-disable-next-line vue/no-mutating-props
      props.pagination.size = val
      emit('sizeChange', val)
    }
    function handleCurrentChange(val: number) {
      // eslint-disable-next-line vue/no-mutating-props
      props.pagination.current = val
      emit('currentChange', val)
    }
    function renderPagination() {
      return (
        <ElPagination
          background 
          layout={props.pagination.layout || 'total, sizes, prev, pager, next, jumper'}
          pager-count={clientWidth < 992 ? 5 : 7}
          page-sizes={props.paginationProps.pageSizes || pageSizes}
          total={props.pagination.total}
          current-page={props.pagination.current}
          page-size={props.pagination.size}
          onSizeChange={(val: number) => handleSizeChange(val)}
          onCurrentChange={(val: number) => handleCurrentChange(val)}
          {...props.paginationProps}
        />
      )
    }

    expose({ elTableRef })

    return () => (
      <div 
        class={[
          'cjui-table', 
          props.tableBorder && 'cjui-table-border',
          props.tableProps && props.tableProps.border && 'cjui-table-border-inner',
        ]}
        style={{
          height: props.height
        }}
        v-loading={props.loading}
      >
        {renderTable()}
        {props.pagination === false ? undefined : renderPagination()}
      </div>
    )
  }
})
</script>

<style lang="scss">
.cjui-table {
  box-sizing: border-box;

  .el-table {
    --el-table-header-text-color: var(--el-text-color-regular);

    .el-table__inner-wrapper {
      height: 100% !important;
    }

    tr {
      --el-table-tr-bg-color: var(--cjui-bg-color-content);
    }

    .el-table__cell {
      padding: 6px 0;
      overflow: hidden;
    }
  }

  .el-pagination {
    padding: 0;
    margin-top: 10px;
    overflow: auto;
  }

  .el-pagination__total.is-first {  
    margin-left: auto !important;
  }
  
  .el-pagination__editor {
    margin-right: 0;
  }

  .el-pagination__classifier {
    margin-left: 0;
  }
}

.cjui-table {
  border-top: 1px solid var(--el-border-color-light);

  .el-table__header-wrapper {
    box-sizing: border-box;
    border-left: 1px solid var(--el-border-color-light);
    border-right: 1px solid var(--el-border-color-light);
  }

  &.cjui-table-border-inner {
    border-top: none;

    .el-table__header-wrapper {
      border-left: none;
      border-right: none;
    }
  }
}

.cjui-table-border {
  border: 1px solid var(--el-border-color-light);
  border-bottom: none;
  position: relative;

  &::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    background-color: var(--el-border-color-light);
  }

  .el-table__header-wrapper {
    border-left: none;
    border-right: none;
  }

  .el-table--border::before,
  .el-table--border::after,
  .el-table__border-left-patch,
  .el-table--border .el-table__inner-wrapper::before {
    display: none;
  }

  th:last-child,
  td:last-child {
    border-right: 0;
  }

  .el-pagination {
    padding-right: 10px;
    padding-bottom: 10px;
  }
}
</style>
